package com.elitesland.metadata.service.impl;

import com.elitesland.core.exception.BusinessException;
import com.elitesland.metadata.service.MetaCatDefService;
import com.elitesland.metadata.service.MetaCatService;
import com.elitesland.metadata.service.MetaCatTableService;
import com.elitesland.metadata.vo.MetaCatDefVO;
import com.elitesland.metadata.vo.MetaCatTreeNodeVO;
import com.elitesland.system.service.SysUdcService;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <pre>作用说明</pre>
 *
 * @author mir
 * @date 11/28/2020 11:55 PM
 */
@Service
public class MetaCatServiceImpl implements MetaCatService {

    private EntityManager em;

    private SysUdcService sysUdcService;

    private MetaCatTableService metaCatTableService;

    private MetaCatDefService metaCatDefService;

    @Autowired
    public void setEm(EntityManager em) {
        this.em = em;
    }

    @Autowired
    public void setSysUdcService(SysUdcService sysUdcService) {
        this.sysUdcService = sysUdcService;
    }

    @Autowired
    public void setMetaCatTableService(MetaCatTableService metaCatTableService) {
        this.metaCatTableService = metaCatTableService;
    }

    @Autowired
    public void setMetaCatDefService(MetaCatDefService metaCatDefService) {
        this.metaCatDefService = metaCatDefService;
    }

    @Override
    public List<MetaCatTreeNodeVO> listCatTree(String table, MetaCatDefVO... fields) {
        val fieldNames = Arrays.stream(fields).map(MetaCatDefVO::getFieldName)
                .collect(Collectors.toList());
        val sb = new StringBuilder("select ");
        for (String field : fieldNames) {
            sb.append(field).append(", ");
        }
        if (sb.length() > 7) {
            sb.delete(sb.length() - 2, sb.length());
        }
        sb.append(" from ").append(table);

        val query = em.createNativeQuery(sb.toString());
        List<Object[]> result = query.getResultList();

        val catTreeNodes = new ArrayList<MetaCatTreeNodeVO>();
        val type1s = result.stream().map(r -> (String) r[0]).distinct().sorted().collect(Collectors.toList());

        val field = fields[0];
        val codeMap = sysUdcService.getCodeMap(field.getDomainCode(), field.getUdcCode());
        type1s.forEach(t1 -> {
            val node = new MetaCatTreeNodeVO();

            node.setCode(t1);
            node.setCodeName(codeMap.getOrDefault(t1, ""));

            val nodes = buildTree(result, t1, 1, fields);
            node.setChildren(nodes);

            catTreeNodes.add(node);
        });

        return catTreeNodes;
    }

    @Override
    public List<MetaCatTreeNodeVO> listCatTreeByTableCode(String code) {
        val tableOpt = metaCatTableService.oneByCode(code);
        if (tableOpt.isEmpty()) {
            throw new BusinessException("自选分类表定义：" + code + ", 不存在");
        }

        val defs = metaCatDefService.listByTableCode(code).stream().toArray(MetaCatDefVO[]::new);

        return listCatTree(tableOpt.get().getTableName(), defs);
    }

    private List<MetaCatTreeNodeVO> buildTree(List<Object[]> result, String pCode, int currentLevel, MetaCatDefVO[] fields) {
        val typeTreeNodes = new ArrayList<MetaCatTreeNodeVO>();

        val types = result.stream().filter(r -> pCode.equalsIgnoreCase((String) r[currentLevel - 1]))
                .map(r -> (String) r[currentLevel])
                .distinct().sorted().collect(Collectors.toList());

        val level = fields.length - 1;
        val field = fields[currentLevel];
        val codeMap = sysUdcService.getCodeMap(field.getDomainCode(), field.getUdcCode());
        types.forEach(t -> {
            val node = new MetaCatTreeNodeVO();
            node.setCode(t);
            node.setCodeName(codeMap.getOrDefault(t, ""));

            if (currentLevel < level) {
                val nodes = buildTree(result, t, currentLevel + 1, fields);
                node.setChildren(nodes);
            }

            typeTreeNodes.add(node);
        });

        return typeTreeNodes;
    }
}
